package com.xtm.exercise.base.A08Collection.A02set;

import org.apache.commons.compress.utils.Sets;
import org.junit.Test;

import java.util.*;

/*
    java.util.Set接口 extends Collection接口
        Set的特点是元素无序，且不可重复   ;	只允许一个 null 元素  ;  HashSet底层实现原理为数组。
        实现类 ： HashSet 、 LinkedHashSet 、 TreeSet
    java.util.HashSet集合 implements Set接口
    HashSet实现类/Set接口特点:  思考维度：顺序、重复、索引、null
        无序，不重复，无索引，一个null,哈希
         1.不允许存储重复的元素
         2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
         3.无序的集合,存储元素和取出元素的顺序有可能不一致
         4.只允许一个 null 元素
         5.底层是一个哈希表结构(查询的速度非常的快)

    说一下 HashSet 的实现原理？
        HashSet底层由HashMap实现
        HashSet的值存放于HashMap的key上
        HashMap的value统一为PRESENT

    Set和Map的使用场景
        如果希望保证插入元素的唯一性，不希望出现重复值，那么可以选择一个 Set 的实现类，比如 HashSet、LinkedHashSet 或者 TreeSet。
        所有 Set 的实现类都遵循了统一约束比如唯一性，而且还提供了额外的特性。比如 TreeSet 还是一个 SortedSet，所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
        如果是希望以键值的形式进行数据存储，那么用Map。根据需要选择Hashtable、HashMap或TreeMap。

    Set与Map主要的不同有两点：Set是继承自Collection的接口类，Set中只存储了Key。
        Set中只存储了key，并且要求key一定要唯一
        Set最大的功能就是对集合中的元素进行去重
        Set中的Key不能修改，如果要修改，先将原来的删除掉，然后再重新插入
        Set中不能插入null的key。

 */
public class Demo01Set {

    /**************************************   HashSet   *******************************************/
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        //使用add方法往集合中添加元素
        set.add(1);
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(null);
        set.add(null);
        //使用迭代器遍历set集合
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            Integer n = it.next();
            System.out.println(n);//1,2,3,null
        }
        //使用增强for遍历set集合
        System.out.println("-----------------");
        for (Integer i : set) {
            System.out.println(i);
        }
    }

    /*
        HashSet存储自定义类型元素
        set集合报错元素唯一:
            存储的元素(String,Integer,...Student,Person...),必须重写hashCode方法和equals方法
            给HashSet中存放自定义类型元素时，需要重写对象中的hashCode和equals方法，建立自己的比较方式，才能保证HashSet集合中的对象唯一
        要求:
            同名同年龄的人,视为同一个人,只能存储一次
     */
    @Test
    public void test01(){
        //创建HashSet集合存储Person
//        HashSet<Person> set = new HashSet<>();
        Set<Person> set = Sets.newHashSet();

        Person p1 = new Person("小美女",18);
        Person p2 = new Person("小美女",18);
        Person p3 = new Person("小美女",19);
        System.out.println(p1.hashCode());// 734175839
        System.out.println(p2.hashCode());// 734175839

        System.out.println(p1==p2);// false
        System.out.println(p1.equals(p2));// true
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(set); // [Person{name='小美女', age=19}, Person{name='小美女', age=18}]
    }

    /**************************************   LinkedHashSet   *******************************************/
    /*
        java.util.LinkedHashSet集合 extends HashSet集合
        LinkedHashSet集合特点:
            底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序
            元素唯一，且有序   它是链表和哈希表组合的一个数据存储结构
            链表结构+Hash表结构，链表结构保证有序，哈希表结构保证不重复，所以LinkedHashSet是有序，不可重复的
     */
    @Test
    public void test02(){
        HashSet<String> set = new HashSet<>();
        set.add("www");
        set.add("abc");
        set.add("abc");
        set.add("itcast");
        System.out.println(set);//[abc, www, itcast] 无序,不允许重复

        if(set.size() > 1){
            System.out.println("有多余两个元素");
        }

        LinkedHashSet<String> linked = new LinkedHashSet<>();
        linked.add("www");
        linked.add("abc");
        linked.add("abc");
        linked.add("itcast");
        System.out.println(linked);//[www, abc, itcast] 有序,不允许重复
    }

    /**
     * 1.定义一个存储数字的集合,并随意存储一些数据(要求这些数据中包含一些重复数字);
     * 2.使用一种很方便的方式将这个集合中重复的数字去掉.
     */
    @Test
    public void test10(){
        List<Integer> intList = new ArrayList<>();
        intList.add(10);
        intList.add(20);
        intList.add(20);
        intList.add(30);
        intList.add(30);

        Set<Integer> intSet = new HashSet<>();
        intSet.addAll(intList);

        System.out.println(intSet);
    }
}
